#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
OpenBMC ABI使用频度分析工具

功能：
1. 克隆OpenBMC生态的GitHub项目
2. 扫描源码中的函数调用
3. 统计每个ABI的使用次数
4. 生成使用频度报告

使用方法：
    python3 usage_analyzer.py --repos repos.txt --output usage_report.xlsx
"""

import json
import sys
import subprocess
import re
from pathlib import Path
from typing import Dict, List, Set, Tuple
from collections import Counter
from dataclasses import dataclass
import tempfile
import shutil

try:
    import pandas as pd
    import openpyxl
    from openpyxl.styles import Font, PatternFill, Alignment
    HAS_EXCEL = True
except ImportError:
    HAS_EXCEL = False


@dataclass
class FunctionUsage:
    """函数使用统计"""
    library: str
    namespace: str
    class_name: str
    function_name: str
    full_signature: str
    usage_count: int
    used_in_repos: List[str]
    used_in_files: List[str]


class UsageAnalyzer:
    """使用频度分析器"""
    
    def __init__(self, abi_data_dir: str, repos_file: str, cache_dir: str = None):
        """
        初始化分析器
        
        参数:
            abi_data_dir: ABI分析数据目录
            repos_file: GitHub仓库列表文件
            cache_dir: 缓存目录（已下载的仓库）
        """
        self.abi_data_dir = Path(abi_data_dir)
        self.repos_file = Path(repos_file)
        self.cache_dir = Path(cache_dir) if cache_dir else None
        self.temp_dir = None
        
        # 加载所有ABI函数
        self.functions = {}  # {function_name: {library, namespace, class, signature}}
        
        # 使用统计
        self.usage_stats = Counter()  # {function_name: count}
        self.usage_details = {}  # {function_name: {repos: [], files: []}}
        
        # 加载ABI数据
        self._load_abi_data()
    
    def _load_abi_data(self):
        """加载所有ABI数据"""
        print("[INFO] 加载ABI数据...")
        
        json_files = list(self.abi_data_dir.glob('*.analyzed.json'))
        
        for json_file in json_files:
            with open(json_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
            
            lib_name = data['library']
            
            for analysis in data['analyses']:
                func_name = analysis['function_name']
                if not func_name:
                    continue
                
                # 收集函数信息
                self.functions[func_name] = {
                    'library': lib_name,
                    'namespace': analysis['namespace'],
                    'class_name': analysis['class_name'],
                    'signature': analysis['demangled_name']
                }
                
                # 初始化使用统计
                self.usage_details[func_name] = {
                    'repos': set(),
                    'files': set()
                }
        
        print(f"[INFO] 已加载 {len(self.functions)} 个函数")
    
    def analyze_repos(self):
        """分析所有仓库"""
        # 读取仓库列表
        if not self.repos_file.exists():
            print(f"[ERROR] 仓库列表文件不存在: {self.repos_file}")
            return
        
        with open(self.repos_file, 'r') as f:
            repos = [line.strip() for line in f if line.strip() and not line.startswith('#')]
        
        print(f"[INFO] 将分析 {len(repos)} 个仓库")
        
        # 创建临时目录
        self.temp_dir = Path(tempfile.mkdtemp(prefix='openbmc_repos_'))
        print(f"[INFO] 临时目录: {self.temp_dir}")
        
        try:
            for idx, repo_url in enumerate(repos, 1):
                print(f"\n[{idx}/{len(repos)}] 分析仓库: {repo_url}")
                self._analyze_repo(repo_url)
        finally:
            # 清理临时目录
            if self.temp_dir and self.temp_dir.exists():
                print(f"\n[INFO] 清理临时目录...")
                shutil.rmtree(self.temp_dir)
    
    def _analyze_repo(self, repo_url: str):
        """分析单个仓库"""
        # 获取仓库名
        repo_name = repo_url.rstrip('/').split('/')[-1].replace('.git', '')
        
        # 检查缓存目录是否已有该仓库
        if self.cache_dir and (self.cache_dir / repo_name).exists():
            print(f"  ✓ 使用缓存: {self.cache_dir / repo_name}")
            repo_path = self.cache_dir / repo_name
        else:
            # 克隆到临时目录
            repo_path = self.temp_dir / repo_name
            
            # 克隆仓库（浅克隆，只要最新代码）
            try:
                print(f"  克隆仓库...")
                result = subprocess.run(
                    ['git', 'clone', '--depth', '1', repo_url, str(repo_path)],
                    capture_output=True,
                    text=True,
                    timeout=300
                )
                
                if result.returncode != 0:
                    print(f"  ✗ 克隆失败: {result.stderr[:200]}")
                    return
                
                print(f"  ✓ 克隆成功")
            except subprocess.TimeoutExpired:
                print(f"  ✗ 克隆超时")
                return
            except Exception as e:
                print(f"  ✗ 克隆失败: {e}")
                return
        
        # 扫描源码文件
        source_files = []
        for ext in ['*.cpp', '*.c', '*.hpp', '*.h', '*.cc', '*.cxx']:
            source_files.extend(repo_path.rglob(ext))
        
        print(f"  找到 {len(source_files)} 个源文件，开始分析...")
        
        # 分析每个文件
        for idx, source_file in enumerate(source_files, 1):
            if idx % 50 == 0:
                print(f"    进度: {idx}/{len(source_files)}")
            self._analyze_file(source_file, repo_name)
        
        print(f"  ✓ 分析完成")
    
    def _analyze_file(self, file_path: Path, repo_name: str):
        """分析单个源文件"""
        try:
            with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                content = f.read()
        except Exception:
            return
        
        # 对每个已知函数进行搜索
        for func_name in self.functions.keys():
            # 使用正则表达式匹配函数调用
            # 匹配模式：func_name( 或 ::func_name( 或 ->func_name( 或 .func_name(
            pattern = rf'(?:[:.]|->|\s){re.escape(func_name)}\s*\('
            
            matches = re.findall(pattern, content)
            
            if matches:
                count = len(matches)
                self.usage_stats[func_name] += count
                self.usage_details[func_name]['repos'].add(repo_name)
                self.usage_details[func_name]['files'].add(str(file_path.relative_to(self.temp_dir)))
    
    def generate_report(self, output_path: str):
        """生成使用频度报告"""
        print("\n[INFO] 生成报告...")
        
        if not HAS_EXCEL:
            print("[ERROR] 需要安装pandas和openpyxl")
            return
        
        # 准备数据
        report_data = []
        
        for func_name, count in self.usage_stats.most_common():
            if count == 0:
                continue
            
            func_info = self.functions[func_name]
            details = self.usage_details[func_name]
            
            report_data.append({
                '函数名': func_name,
                '库': func_info['library'],
                '命名空间': func_info['namespace'],
                '类': func_info['class_name'],
                '完整签名': func_info['signature'][:100],
                '使用次数': count,
                '使用仓库数': len(details['repos']),
                '使用文件数': len(details['files']),
                '使用仓库': ', '.join(sorted(details['repos']))[:100],
            })
        
        # 添加未使用的函数（使用次数为0）
        for func_name, func_info in self.functions.items():
            if func_name not in self.usage_stats or self.usage_stats[func_name] == 0:
                report_data.append({
                    '函数名': func_name,
                    '库': func_info['library'],
                    '命名空间': func_info['namespace'],
                    '类': func_info['class_name'],
                    '完整签名': func_info['signature'][:100],
                    '使用次数': 0,
                    '使用仓库数': 0,
                    '使用文件数': 0,
                    '使用仓库': '',
                })
        
        df = pd.DataFrame(report_data)
        
        # 保存到Excel
        output_file = Path(output_path)
        output_file.parent.mkdir(parents=True, exist_ok=True)
        
        with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
            # Sheet 1: 按使用频度排序
            df_sorted = df.sort_values('使用次数', ascending=False)
            df_sorted.to_excel(writer, sheet_name='按使用频度', index=False)
            
            # Sheet 2: Top 100
            df_top100 = df_sorted.head(100)
            df_top100.to_excel(writer, sheet_name='Top100高频函数', index=False)
            
            # Sheet 3: Top 500
            df_top500 = df_sorted.head(500)
            df_top500.to_excel(writer, sheet_name='Top500高频函数', index=False)
            
            # Sheet 4: 未使用的函数
            df_unused = df[df['使用次数'] == 0].sort_values('库')
            df_unused.to_excel(writer, sheet_name='未使用函数', index=False)
            
            # Sheet 5: 按库统计
            lib_stats = df.groupby('库').agg({
                '使用次数': 'sum',
                '函数名': 'count'
            }).reset_index()
            lib_stats.columns = ['库', '总使用次数', '函数数']
            lib_stats = lib_stats.sort_values('总使用次数', ascending=False)
            lib_stats.to_excel(writer, sheet_name='按库统计', index=False)
        
        # 美化Excel
        self._format_excel(output_file)
        
        print(f"[SUCCESS] 报告已生成: {output_path}")
        
        # 打印统计
        self._print_summary(df)
    
    def _print_summary(self, df: pd.DataFrame):
        """打印统计摘要"""
        print("\n" + "=" * 80)
        print("使用频度统计摘要")
        print("=" * 80)
        
        total_functions = len(df)
        used_functions = len(df[df['使用次数'] > 0])
        unused_functions = len(df[df['使用次数'] == 0])
        
        print(f"总函数数: {total_functions}")
        print(f"被使用的函数: {used_functions} ({used_functions/total_functions*100:.1f}%)")
        print(f"未使用的函数: {unused_functions} ({unused_functions/total_functions*100:.1f}%)")
        
        print(f"\nTop 10 高频函数:")
        top10 = df.nlargest(10, '使用次数')
        for idx, row in top10.iterrows():
            print(f"  {row['函数名']}: {row['使用次数']}次 ({row['库']})")
        
        print("\n按库使用统计:")
        lib_usage = df.groupby('库')['使用次数'].sum().sort_values(ascending=False)
        for lib, count in lib_usage.head(10).items():
            print(f"  {lib}: {count}次")
        
        print("=" * 80)
    
    def _format_excel(self, excel_path: Path):
        """美化Excel表格"""
        wb = openpyxl.load_workbook(excel_path)
        
        header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
        header_font = Font(bold=True, color="FFFFFF")
        
        for sheet in wb.worksheets:
            # 设置表头样式
            if sheet.max_row > 0:
                for cell in sheet[1]:
                    cell.fill = header_fill
                    cell.font = header_font
                    cell.alignment = Alignment(horizontal='center', vertical='center')
            
            # 自动调整列宽
            for column in sheet.columns:
                max_length = 0
                column_letter = column[0].column_letter
                for cell in column:
                    try:
                        if cell.value:
                            max_length = max(max_length, len(str(cell.value)))
                    except:
                        pass
                adjusted_width = min(max_length + 2, 80)
                sheet.column_dimensions[column_letter].width = adjusted_width
        
        wb.save(excel_path)


def main():
    """主函数"""
    import argparse
    
    parser = argparse.ArgumentParser(description='OpenBMC ABI使用频度分析工具')
    parser.add_argument('--abi-dir', default='output/analyzed', help='ABI分析数据目录')
    parser.add_argument('--repos', default='config/openbmc_repos.txt', help='GitHub仓库列表文件')
    parser.add_argument('--output', default='output/OPENBMC_USAGE_ANALYSIS.xlsx', help='输出Excel文件路径')
    parser.add_argument('--cache-dir', default='data/repos', help='仓库缓存目录')
    
    args = parser.parse_args()
    
    try:
        # 创建分析器
        analyzer = UsageAnalyzer(args.abi_dir, args.repos, args.cache_dir)
        
        # 分析仓库
        analyzer.analyze_repos()
        
        # 生成报告
        analyzer.generate_report(args.output)
        
        return 0
    
    except Exception as e:
        print(f"[ERROR] 分析失败: {e}", file=sys.stderr)
        import traceback
        traceback.print_exc()
        return 1


if __name__ == '__main__':
    sys.exit(main())

